#pragma once

#include <memory>
#include <unordered_map>
#include "Epoller.hpp"
#include "Connection.hpp"
#include "Log.hpp"

using namespace LogModule;

class Reactor
{
    static const int revs_num = 128;

private:
    bool IsConnectionExistsHelper(int sockfd)
    {
        auto iter = _connections.find(sockfd);
        if (iter == _connections.end())
            return false;
        else
            return true;
    }

    bool IsConnectionExists(std::shared_ptr<Connection> &conn)
    {
        return IsConnectionExistsHelper(conn->GetSockFd());
    }

    bool IsConnectionExists(int sockfd)
    {
        return IsConnectionExistsHelper(sockfd);
    }

    bool IsConnectionEmpty()
    {
        return _connections.empty();
    }

    int LoopOnce(int timeout)
    {
        return _epoller_ptr->WaitEvents(_revs, revs_num, timeout);
    }

    void Distpatcher(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int sockfd = _revs[i].data.fd;
            uint32_t revents = _revs[i].events;

            if (revents & EPOLLERR)
            {
                revents |= (EPOLLIN | EPOLLOUT);
            }
            if (revents & EPOLLHUP)
            {
                revents |= (EPOLLIN | EPOLLOUT);
            }
            if (revents & EPOLLIN)
            {
                if (IsConnectionExists(sockfd))
                    _connections[sockfd]->Recver();
            }
            if (revents & EPOLLOUT)
            {
                if (IsConnectionExists(sockfd))
                    _connections[sockfd]->Sender();
            }
        }
    }

public:
    Reactor()
        : _epoller_ptr(std::make_unique<Epoller>()),
          _isrunning(false)
    {
    }

    void Loop()
    {
        if (IsConnectionEmpty())
            return;
        _isrunning = true;
        int timeout = 1000;

        while (_isrunning)
        {
            PrintConnection();
            int n = LoopOnce(timeout);
            Distpatcher(n);
        }
        Stop();
    }

    void AddConnection(std::shared_ptr<Connection> &conn)
    {
        if (IsConnectionExists(conn))
        {
            LOG(LogLevel::WARNING) << "conn is exists: " << conn->GetSockFd();
            return;
        }

        uint32_t events = conn->GetEvent();
        int sockfd = conn->GetSockFd();
        _epoller_ptr->AddEvent(sockfd, events);

        conn->SetOwner(this);

        _connections[sockfd] = conn;
    }

    void EnableReadWrite(int sockfd, bool enableread, bool enablewrite)
    {
        if (!IsConnectionExists(sockfd))
        {
            LOG(LogLevel::WARNING) << "EnableReadWrite, conn is exists: " << sockfd;
            return;
        }

        uint32_t new_event = (EPOLLET | (enableread ? EPOLLIN : 0) | (enablewrite ? EPOLLOUT : 0));
        _connections[sockfd]->SetEvent(new_event);

        _epoller_ptr->ModEvent(sockfd, new_event);
    }

    void DelConnection(int sockfd)
    {
        _epoller_ptr->DelEvent(sockfd);
        _connections.erase(sockfd);
        close(sockfd);
    }

    void Stop()
    {
        _isrunning = false;
    }

    void PrintConnection()
    {
        std::cout << "当前 Reactor 正在管理的 fd List: ";

        for (auto &conn : _connections)
        {
            std::cout << conn.second->GetSockFd() << " ";
        }
        std::cout << std::endl;
    }

    ~Reactor()
    {
    }

private:
    std::unique_ptr<Epoller> _epoller_ptr;

    std::unordered_map<int, std::shared_ptr<Connection>> _connections;

    struct epoll_event _revs[revs_num];
    bool _isrunning;
};